/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.

1、Promise是一个类
  在执行这个类的时候需要传递一个执行器进去，执行器会立即执行
2、new Promise的时候有两个参数：resolve、reject
3、Promise有三种状态：pending（等待）、fulfilled（成功）、rejected（失败）
   通过resolve改变成功、reject改变失败
4、只能从等待 => 成功 或者 等待 => 失败，一般转变不会再次改变
5、	then方法内部做的事情就是判断状态
	如果状态是成功 => 调用成功的回调
	如果状态是失败 => 调用失败的回调
	被定义在原型对象上的一个方法
	then 成功回调有一个参数
	then方法可以链式调用，每个then方法返回一个Promise对象
   多次调用时，把上一个返回的值传递给下一次调用，可以解决深层次嵌套回调（例如：多层setTimeout嵌套调用）
6、finally方法是一定会执行的一个类方法   
7、catch方法中处理promise失败的情况
8、Promise.all方法中的参数是一个数组，传递参数顺序与返回结果保持一致
   当参数中的所有请求均成功时，才会返回一个成功的promise对象，否则返回失败状态的promise对象

*/

// 定义成常量：复用、代码有提示
const PENDING = 'pending'; // 等待状态
const FULFILLED = 'fulfilled'; // 成功状态
const REJECTED = 'rejected'; // 失败状态



function resolvePromise(promise2, x, resolve, reject) {
	// 代表返回了自身, 传递类型错误, 避免循环调用
	if (promise2 === x) {
		return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
	}
	if (x instanceof MyPromise) {
		// x 是一个Promise 对象
		// 在promise状态改变后, 传递值
		x.then(resolve, reject)
	} else {
		// x 是一个普通值
		resolve(x)
	}
}
// Promise是一个类
class MyPromise {
	constructor(executor) {
		try {
			// 该执行器会立即调用, 并且接收两个参数, 分别为resolve和reject
			executor(this.resolve, this.reject)
		} catch (error) {
			// 当执行器出错时, 使用reject函数处理
			this.reject(error)
		}
	}

	// promise 状态, 初始化为pending状态
	status = PENDING

	// 成功之后的值, 默认为undefined
	value = undefined

	// 失败之后的原因, 默认为undefined
	reason = undefined

	// 暂存的成功回调函数, 数组可以存储多个注册的回调函数
	successCallback = []

	// 暂存的失败回调函数, 数组可以存储多个注册的回调函数
	failCallback = []

	// 使用箭头函数固定this指向为声明时候的上下文环境;
	// resolve的作用是更改状态,从pending转变为fulfilled
	resolve = value => {
		// 只有Promise的状态是pending,才可以进行更改状态的操作
		if (this.status === PENDING) {
			// 将状态更改为成功
			this.status = FULFILLED

			// 保存成功之后的值, 方便在之后的then方法中使用
			this.value = value

			// 更改为成功状态后, 依次遍历之前暂存的成功回调函数,并执行
			this.successCallback.forEach(cb => cb())
		}
	}

	reject = reason => {
		// 只有Promise的状态是pending,才可以进行更改状态的操作
		if (this.status === PENDING) {
			// 将状态更改为失败
			this.status = REJECTED

			// 保存失败之后的原因, 方便在之后的then方法中使用
			this.reason = reason

			// 更改为失败状态后, 依次遍历之前暂存的失败回调函数,并执行
			this.failCallback.forEach(cb => cb())
		}
	}

	then(successCallback, failCallback) {
		// 当回调函数不存在或者不是函数的时候, 直接传递值给下一层
		successCallback = typeof successCallback === 'function' ? successCallback : value => value
		failCallback =
			typeof failCallback === 'function'
				? failCallback
				: reason => {
						throw reason
				  }

		const promise2 = new MyPromise((resolve, reject) => {
			// 首先判断状态
			if (this.status === FULFILLED) {
				// 异步执行, 获取promise2
				setTimeout(() => {
					try {
						// 返回,成功回调的返回值
						const x = successCallback(this.value)
						// 判断x是普通值, 还是一个promise对象
						// 如果x是普通值, 直接调用resolve
						// 如果x是一个promise对象, 进一步查看promise对象的返回结果
						// 根据promise对象的返回结果, 决定进一步调用resolve还是reject
						resolvePromise(promise2, x, resolve, reject)
					} catch (error) {
						// 当成功回调函数有出错时, 需要传递错误信息给下一个promise
						reject(error)
					}
				}, 0)
			} else if (this.status === REJECTED) {
				// 异步执行, 获取promise2
				setTimeout(() => {
					try {
						// 返回,失败回调的返回值
						const x = failCallback(this.reason)
						// 判断x是普通值, 还是一个promise对象
						// 如果x是普通值, 直接调用resolve
						// 如果x是一个promise对象, 进一步查看promise对象的返回结果
						// 根据promise对象的返回结果, 决定进一步调用resolve还是reject
						resolvePromise(promise2, x, resolve, reject)
					} catch (error) {
						// 当成功回调函数有出错时, 需要传递错误信息给下一个promise
						reject(error)
					}
				}, 0)
			} else {
				// 当前的状态为pending状态, 代表promise容器中的异步任务还未执行完
				// 暂存成功与失败的回调函数, 待状态明确后再遍历调用
				this.successCallback.push(() => {
					setTimeout(() => {
						try {
							// 返回,失败回调的返回值
							const x = successCallback(this.value)
							// 判断x是普通值, 还是一个promise对象
							// 如果x是普通值, 直接调用resolve
							// 如果x是一个promise对象, 进一步查看promise对象的返回结果
							// 根据promise对象的返回结果, 决定进一步调用resolve还是reject
							resolvePromise(promise2, x, resolve, reject)
						} catch (error) {
							// 当成功回调函数有出错时, 需要传递错误信息给下一个promise
							reject(error)
						}
					}, 0)
				})
				this.failCallback.push(() => {
					setTimeout(() => {
						try {
							// 返回,失败回调的返回值
							const x = failCallback(this.reason)
							// 判断x是普通值, 还是一个promise对象
							// 如果x是普通值, 直接调用resolve
							// 如果x是一个promise对象, 进一步查看promise对象的返回结果
							// 根据promise对象的返回结果, 决定进一步调用resolve还是reject
							resolvePromise(promise2, x, resolve, reject)
						} catch (error) {
							// 当失败回调函数有出错时, 需要传递错误信息给下一个promise
							reject(error)
						}
					}, 0)
				})
			}
		})

		// 通过返回promise对象, 实现then方法的链式调用
		return promise2
	}

	catch(failCallback) {
		return this.then(undefined, failCallback)
	}

	finally(callback) {
		// 通过执行then方法拿到promise状态, 并在then的成功回调和失败回调中都执行callback
		// 返回的是一个promise对象
		// 传递成功值和失败原因到下一个then方法中
		// 通过MyPromise.resolve方法转换为promise对象,来处理callback返回值为promie对象时的状况
		return this.then(
			value => MyPromise.resolve(callback()).then(() => value),
			reason =>
				MyPromise.resolve(callback()).then(() => {
					throw reason
				})
		)
	}

	static all(array = []) {
		const results = []
		let index = 0 // 标识完成的操作数量
		return new MyPromise((resolve, reject) => {
			// 定义一个方法addData, 用于将对应的执行结果保存在对应的位置
			const addData = (key, value) => {
				results[key] = value
				index++
				// 添加后, 判断下是否结束完所有的异步操作, 如果完成,则传递结果数组
				if (index === array.length) {
					// 循环执行结束后,传递结果数组
					resolve(results)
				}
			}
			// 如果参数长度为0，则直接同步返回一个 resolved 状态的 promise
			if (!array.length) {
				resolve(results)
				return
			}
			// 循环遍历all的参数, 并分别判断是什么类型的值, 将执行的结果存入结果数组中
			array.forEach((current, index) => {
				if (current instanceof MyPromise) {
					// promise对象
					current.then(
						value => addData(index, value),
						reason => reject(reason)
					)
				} else {
					// 普通值
					addData(index, current)
				}
			})
		})
	}

	static race(array) {
		return new MyPromise((resolve, reject) => {
			// 如果参数长度为0，则返回一个永远等待状态的 promise
			if (!array.length) {
				return
			}

			// 遍历包装所有值为promise对象,方便统一处理
			// 当某一个promise对象执行完毕后立即调用回调函数,改变整个返回promise的状态
			array.forEach(current => {
				MyPromise.resolve(current).then(resolve, reject)
			})
		})
	}

	static any(array) {
		let index = 0 // 标识完成的操作数量
		return new MyPromise((resolve, reject) => {
			// 如果参数长度为0，则返回一个成功状态的 promise
			if (!array.length) {
				return resolve()
			}

			const changeIndex = () => {
				index++
				// 添加后, 判断下是否结束完所有的异步操作, 如果完成,则传递失败结果
				if (index === array.length) {
					// 循环执行结束后,表示所有结果均失败, 传递失败原因, 暂且先传递一个普通错误对象
					reject(new Error('AggregateError'))
				}
			}

			// 遍历包装所有值为promise对象,方便统一处理
			// 当某一个promise对象执行完毕并且成功后立即调用成功回调函数.
			// 当遍历执行完毕后, 如果没有成功的, 则改变状态为失败
			array.forEach(current => {
				MyPromise.resolve(current).then(resolve, () => changeIndex())
			})
		})
	}

	static resolve(value) {
		return new MyPromise((resolve, reject) => {
			if (value instanceof MyPromise) {
				// 如果是promise对象,直接返回对应的处理状态
				return value.then(resolve, reject)
			} else {
				// 如果不是,直接传递结果
				resolve(value)
			}
		})
		
	}

	static reject(err) {
		return new MyPromise((resolve, reject) => {
			reject(err)
		})
	}
}

// 下面是测试
let r = new MyPromise((resovle, reject)=>{
    resovle('成功')
})

r.then(value=>{
    console.log(value,'this is resolve value');
})

// MyPromise.race(['p1']).then(value => console.log(value))
//MyPromise.any([p1(), p2()]).then(value => console.log(value)).catch(reason => console.log(reason))